﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace RayTracer.Kernels
{
    /// <summary>
    /// Beinhaltet den OpenCL Code der Kernel.
    /// </summary>
    partial class KernelSumColor
    {
        private const string KERNEL_MAIN = "SumColor";

        /*************
        *  
        *  Eingabe:
        *  eyeDir      Die Einfallsrichtung des Ausgangsstrahls.[i]
        *  reflCol     Die Farbe des refl. Strahls.             [i]
        *  reflDir     Die Richtung des refl. Strahls.          [i]
        *  refrCol     Die Farbe des refr. Strahls.             [i]
        *  refrDir     Die Richtung des refr. Strahls.          [i]
        *  normal      Die Normale am Schnittpunkt.             [i]
        *  
        *  objMat      Gibt das Objektmaterial an.              [i]
        *  ligInVis    Die Sichtbarkeit des Lichts.             [i*ligCount+l]
        *  ligDir      Die Einfallsrichtung des Lichts          [i*ligCount+l]
        *  ligColor    Die Farbe des Lichts                     [l]
        *  ligCount    Die Anzahl der Lichter L
        *  
        * 
        *  Ausgabe:
        *  color       Die Farbe des Strahls.                   [i]
        *  
        *************/
        private const string KERNEL_SOURCE = @"
            kernel void SumColor(
                global  read_only float4* eyeDir,
                global  read_only float4* reflCol,
                global  read_only float4* reflDir,
                global  read_only float* reflDist,
                global  read_only float4* refrCol,
                global  read_only float4* refrDir,
                global  read_only float* refrDist,
                global  read_only float4* normal,

                global  read_only float16* objMat,
                global  read_only bool* ligInVis,
                global  read_only float4* ligDir,
                global  read_only float4* ligColor,
                int ligCount,

                global  write_only float4* color
            )
            {
                int i = get_global_id(0);
                
                float cosRho = 0.0f;
                float4 ambientColor = (float4)(objMat[i].s7 * objMat[i].s0, 
                                               objMat[i].s7 * objMat[i].s1, 
                                               objMat[i].s7 * objMat[i].s2, 
                                               1.0f);
                float4 diffuseColor = (float4)(0.0f,0.0f,0.0f,0.0f);
                float4 specularColor = (float4)(0.0f,0.0f,0.0f,0.0f);

                //Lokales Beleuchtungsmodell
                for(int l = 0; l < ligCount; l++){
                    if(!ligInVis[i*ligCount+l]) {    //Falls das Licht nicht verdeckt ist, addiere die diffuse Farbe

                        //Diffuse Beleuchtung
                        cosRho = dot(normal[i], ligDir[i*ligCount+l]);
                        if(cosRho >= 0.0f) {
                            //Diffuse Reflektion nach Lambert
                            diffuseColor += (float4)(cosRho * objMat[i].s4 * ligColor[l].s0,
                                                     cosRho * objMat[i].s5 * ligColor[l].s1,
                                                     cosRho * objMat[i].s6 * ligColor[l].s2,
                                                     1.0f);
                        
                            //Spiegelnde Reflektion durch das Phong-Modell
                            float4 reflectDir = ligDir[i*ligCount+l] - 2.0f * dot(ligDir[i*ligCount+l],normal[i]) * normal[i];
                            float phongTerm = dot(reflectDir, eyeDir[i]);
                            if(phongTerm >= 0.0f && objMat[i].s8 >0.0f && objMat[i].s9 > 0.0f && objMat[i].sa > 0.0f) {
                               float specularpower = pow(phongTerm, objMat[i].sc);
                               specularColor += (float4)(specularpower,specularpower,specularpower,1.0f);
                            }

                            /*//Auskommentiert: Alternative spiegelnde Reflektion durch das Hall-Modell
                            // Ohne fresnelschen Term (siehe A. S. Glassner S.152ff)
                            float4 angleBisector = (float4)(ligDir[i*ligCount+l].x - eyeDir[i].x, 
                                                            ligDir[i*ligCount+l].y - eyeDir[i].y, 
                                                            ligDir[i*ligCount+l].z - eyeDir[i].z,
                                                            0.0f);
                            float cosAlpha = dot(normal[i], angleBisector/length(angleBisector));
                            if(cosAlpha >= 0.0f) {
                                cosAlpha = pow(cosAlpha, objMat[i].sc);
                                specularColor += (float4)(objMat[i].s8 * cosAlpha,
                                                          objMat[i].s9 * cosAlpha,
                                                          objMat[i].sa * cosAlpha,
                                                          1.0f);
                            }*/
                        }
                    }
                }
               
                //Reflektion von anderen Objekten nach Hall
                float4 reflectionColor = (float4)(0.0f, 0.0f, 0.0f, 1.0f); //Sammelt die Farbe die von anderen Körpern reflektiert und durchgelassen wird.
                if(reflDist[i] > 0.0f && objMat[i].sd>0.0f){
                   float fallOffTerm = 1/(1+reflDist[i]);
                   reflectionColor = (float4)(objMat[i].s8 * reflCol[i].s0 * fallOffTerm,
                                              objMat[i].s9 * reflCol[i].s1 * fallOffTerm,
                                              objMat[i].sa * reflCol[i].s2 * fallOffTerm,
                                              1.0f);
                }
                
                //Refraktion von anderen Objekten nach Hall
                float4 transmissionColor = (float4)(0.0f, 0.0f, 0.0f, 1.0f);//Sammelt die Farbe die von anderen Körpern reflektiert und durchgelassen wird.
                if(refrDist[i] > 0.0f && objMat[i].sf> 0.0f && refrCol[i].s0 >0.0f && refrCol[i].s1 > 0.0f && refrCol[i].s2 > 0.0f){
                    // Beer's law anwenden
			        float absorbanceFactor =  -0.15f * refrDist[i];
				    float4 transparency = (float4)(exp(absorbanceFactor * objMat[i].s9), 
                                                   exp(absorbanceFactor * objMat[i].s9), 
                                                   exp(absorbanceFactor * objMat[i].sa), 
                                                   1.0f);
                    transmissionColor = (float4)(refrCol[i].s0 * transparency.s0,
                                                 refrCol[i].s1 * transparency.s1,
                                                 refrCol[i].s2 * transparency.s2,
                                                 1.0f); //multiplied by Specular TransmissionFactor

                } 
                //Summierung aller Bestandteile des Shader-Modells von Hall
                color[i] = (objMat[i].s7*diffuseColor)  
                           + (objMat[i].sd*specularColor)  
                           + (objMat[i].sd*reflectionColor)  
                           + (objMat[i].sf*transmissionColor)  
                           + ambientColor*0.1f
                           ;
            }
            ";
    }
}
